/* 
 * 
 * Author: Jakub Krajniak (c) 2006-2007
 * Contact: jkrajniak@gmail.com
 * Licence: GPL
 *
 * 
 * This program try to build Mealy automate from filtron's description
 * It's support only manual creating. Automating process probably would
 * be support in future version. 
 * 
 * Some comments for source are in polish. Very sorry for that unconvince. 
 * If you have question, send mail to me.
 * 
 * 
 * 
 * 
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Filtron
{
    /// <summary>
    /// Parsing ML file
    /// </summary>
    public class MlFile
    {
        public MlFile()
        {
        }

        public MlFile(string[] input)
        {
            int counter = 0;
            int states = 0;
            int enters = 0;
            foreach (string line in input)
            {
                if (counter == 0)
                    if (line != "ML")
                        throw new Exception("Invalid file format");
                if (counter == 1)
                {
                    states = Convert.ToInt32(line.Split(' ')[0]);
                    enters = Convert.ToInt32(line.Split(' ')[1]);
                }
                if (counter > 1)
                {
                    string[] st = line.Split(' ');
                    int new_state = Convert.ToInt32(st[0]);
                    Dictionary<int, int> tmp_output = new Dictionary<int, int>();
                    Dictionary<int, int> tmp_next = new Dictionary<int, int>();
                    int enter_counter = 0;
                    foreach (char z in st[1])
                    {
                        int decode_symbol = 0;
                        decode_symbol = alp.decode(z);
                        tmp_next.Add(enter_counter, decode_symbol);
                        enter_counter++;
                    }
                    enter_counter = 0;
                    foreach (char z in st[2])
                    {
                        int decode_symbol = 0;
                        decode_symbol = alp.decode(z);
                        if (!mySymbolUsed.Contains(z))
                            mySymbolUsed.Add(z);
                        tmp_output.Add(enter_counter, decode_symbol);
                        enter_counter++;
                    }
                    myNextStates.Add(new_state, tmp_next);
                    myOutput.Add(new_state, tmp_output);

                }
                counter++;
            }
        }

        public MlFile(Dictionary<int, Dictionary<int, int>> next_state, Dictionary<int, Dictionary<int, int>> output)
        {
            myNextStates = next_state;
            myOutput = output;
            foreach (KeyValuePair<int, Dictionary<int, int>> ke in myOutput)
            {
                foreach (KeyValuePair<int, int> ku in ke.Value)
                    if (!mySymbolUsed.Contains(alp.code(ku.Value).ToCharArray()[0]))
                        mySymbolUsed.Add(alp.code(ku.Value).ToCharArray()[0]);
            }
        }

        public string[] GetFile()
        {
            List<string> outSt = new List<string>();
            outSt.Add("ML");
            outSt.Add(myNextStates.Keys.Count.ToString() + " " + myNextStates[0].Keys.Count.ToString() + " " + mySymbolUsed.Count.ToString());
            for (int i = 0; i < myNextStates.Keys.Count; i++)
            {
                string line = i.ToString() + " ";
                foreach (KeyValuePair<int, int> kk in myNextStates[i])
                {
                    line += alp.code(kk.Value);
                }
                line += " ";
                foreach (KeyValuePair<int, int> kk in myOutput[i])
                {
                    line += alp.code(kk.Value);
                }
                outSt.Add(line);
            }
            return outSt.ToArray();
        }

        public Dictionary<int, Dictionary<int, int>> NextStates
        {
            get { return myNextStates; }
        }

        public Dictionary<int, Dictionary<int, int>> Outputs
        {
            get { return myOutput; }
        }



        private Dictionary<int, Dictionary<int, int>> myNextStates = new Dictionary<int, Dictionary<int, int>>();
        private Dictionary<int, Dictionary<int, int>> myOutput = new Dictionary<int, Dictionary<int, int>>();
        private Alphabet alp = new Alphabet(64);
        private List<char> mySymbolUsed = new List<char>(); // of course for nothing

    }
    /// <summary>
    /// struct used by Alphabet class
    /// </summary>
    public struct abc_pole
    {
        public System.Drawing.Color color;
        public string symbol;
        public abc_pole(System.Drawing.Color m_color, string m_symbol)
        {
            color = m_color;
            symbol = m_symbol;
        }
        public static bool operator ==(abc_pole a, abc_pole b)
        {
            if ((a.color == b.color) && (a.symbol == b.symbol))
                return true;
            else
                return false;
        }
        public static bool operator !=(abc_pole a, abc_pole b)
        {
            if ((a.color != b.color) || (a.symbol != b.symbol))
                return true;
            else
                return false;
        }
    };
    /// <summary>
    /// Main class for decode and encode alphabet
    /// </summary>
    public class Alphabet
    {
        private Dictionary<int, abc_pole> m_alphabet = new Dictionary<int, abc_pole>();
        private Dictionary<string, int> m_revers = new Dictionary<string, int>();
        private int last_key = 0;
        private const string alp = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz|+";
        public Alphabet(int args)
        {

            Add(new abc_pole(System.Drawing.Color.White, "0"));
            Add(new abc_pole(System.Drawing.Color.Lime, "1"));
            System.Array colorsArray = Enum.GetValues(typeof(System.Drawing.KnownColor));
            System.Drawing.KnownColor[] allColors = new System.Drawing.KnownColor[colorsArray.Length];

            Array.Copy(colorsArray, allColors, colorsArray.Length);

            for (int i = 2; i < args; i++)
            {
                Add(new abc_pole(System.Drawing.Color.FromName(allColors[i + 30].ToString()), alp[i].ToString()));
                colors.Add(System.Drawing.Color.FromName(allColors[i + 30].ToString()));
            }
        }
        public Alphabet()
        {

            Add(new abc_pole(System.Drawing.Color.White, "0"));
            Add(new abc_pole(System.Drawing.Color.Lime, "1"));
            System.Array colorsArray = Enum.GetValues(typeof(System.Drawing.KnownColor));
            System.Drawing.KnownColor[] allColors = new System.Drawing.KnownColor[colorsArray.Length];
            Array.Copy(colorsArray, allColors, colorsArray.Length);
            for (int i = 2; i < 64; i++)
            {
                Add(new abc_pole(System.Drawing.Color.FromName(allColors[i + 30].ToString()), alp[i].ToString()));
                colors.Add(System.Drawing.Color.FromName(allColors[i + 30].ToString()));
            }
        }
        public abc_pole Get(int idx)
        {
            abc_pole pole = new abc_pole();
            if (m_alphabet.ContainsKey(idx))
                pole = m_alphabet[idx];
            return pole;
        }
        /// <summary>
        /// z int na symbol alfabetu
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public string code(int idx)
        {
            if (idx == -1)
                return "-";
            else
                return alp[idx].ToString();
        }
        /// <summary>
        /// z symbolu na int
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public int decode(string idx)
        {
            if (!alp.Contains(idx))
                throw new MyErrors("Unsupported char: " + idx + "\nUse only 0-9,a-z,A-Z,|,+");
            if (idx == "-")
                return -1;
            else
                if (idx != null)
                    return alp.IndexOf(idx);
                else
                    return 0;
        }
        public int decode(char idx)
        {
            if (idx == '-')
                return -1;
            else
                if (idx != null)
                    return alp.IndexOf(idx.ToString());
                else
                    return 0;
        }
        public int Get(string st)
        {
            int ret = -1;
            if (m_revers.ContainsKey(st))
                ret = m_revers[st];
            return ret;
        }
        public int Add(abc_pole pole)
        {
            bool finded = false;
            int finded_symbol = 0;
            int number_added = 0;
            foreach (KeyValuePair<int, abc_pole> key in m_alphabet)
            {
                if (key.Value.symbol == pole.symbol)
                {
                    finded = true;
                    finded_symbol = key.Key;
                }
            }
            if (finded)
            {
                m_alphabet[finded_symbol] = pole;
                number_added = finded_symbol;
            }
            if (!finded)
            {
                if (last_key <= 63 && (!m_alphabet.ContainsValue(pole)) && (!find_color(pole)))
                {
                    m_alphabet.Add(last_key, pole);
                    number_added = last_key;
                    m_revers.Add(pole.symbol, last_key);
                    last_key++;
                }
            }
            return number_added;
        }

        public bool find_color(abc_pole pole)
        {
            bool ret = false;
            foreach (KeyValuePair<int, abc_pole> key in m_alphabet)
            {
                if (key.Value.color == pole.color)
                    ret = true;
            }
            return ret;
        }

        public bool Remove(int idx)
        {
            bool ret = false;
            if (m_alphabet.ContainsKey(idx))
            {
                m_alphabet.Remove(idx);
                last_key--;
                ret = true;
            }
            return ret;
        }
        public void Remove(abc_pole pole)
        {
            if (!m_alphabet.ContainsValue(pole))
                foreach (KeyValuePair<int, abc_pole> key in m_alphabet)
                    if (key.Value == pole)
                    {
                        m_alphabet.Remove(key.Key);
                        last_key--;
                    }
        }
        /// <summary>
        /// Returning all symbols from alphabet
        /// </summary>
        /// <returns></returns>
        public List<string> All_symbols()
        {
            List<string> temp_list = new List<string>();
            foreach (string key in m_revers.Keys)
            {
                temp_list.Add(key);
            }
            return temp_list;
        }
        public System.Drawing.Color[] usedColors
        {
            get
            {
                return colors.ToArray();
            }
        }
        private List<System.Drawing.Color> colors = new List<System.Drawing.Color>();
    }
    public class CEFAM
    {
        public CEFAM()
        {
        }
        public CEFAM(string[] file_input)
        {
            int counter = 0;
            int Aseg = 0;
            int hSymbolCounter = 0;
            int kCounter = 0;
            extAutomate = new List<Regex>();
            actAutomate = new List<string>();
            foreach (string line in file_input)
            {
                if (counter == 0)
                    if (!line.Contains("CEFAM"))
                        throw new Exception("Not valid CEFAM format");
                try
                {
                    if (counter == 1)
                    {
                        kSegment = Convert.ToInt32(line.Split(' ')[1]);
                        Aseg = Convert.ToInt32(line.Split(' ')[0]);
                        op = new OperationSet(kSegment, Aseg);
                    }
                    else
                        if (counter == 2)
                            foreach (char c in line)
                                actAutomate.Add(c.ToString());
                        else
                            if (counter > 2 && counter <= Aseg + 3) // read h functions
                            {
                                string jSymbol = alp.All_symbols()[hSymbolCounter];
                                List<string> rr = new List<string>();
                                foreach (char z in line)
                                {
                                    rr.Add(z.ToString());
                                }
                                Grid.Add(rr);
                                op.PutHLine(line, jSymbol);
                                hSymbolCounter++;
                            }
                            else
                                if (counter > Aseg + 3 && counter < kSegment + 3 + Aseg)
                                {
                                    List<string> rr = new List<string>();
                                    foreach (char z in line)
                                    {
                                        rr.Add(z.ToString());
                                    }
                                    Grid.Add(rr);
                                    op.PutFLine(line, kCounter);
                                    kCounter++;
                                }
                                else
                                    if (counter > 0 && counter >= kSegment + 3 + Aseg)
                                        extAutomate.Add(new Regex("^" + line.Replace('*', '?')));
                    counter++;
                }
                catch (Exception er)
                {
                    throw new MyErrors("Not valid CEFAM format\n"+er.Message);
                }
            }
        }
        public CEFAM(OperationSet ofcaOperacje, List<Regex> extA, List<string> actA, int kS)
        {
            op = ofcaOperacje;
            extAutomate = extA;
            actAutomate = actA;
            kSegment = kS;
        }
        public string[] GetFile()
        {
                List<string> ret = new List<string>();
                ret.Add("CEFAM");
                ret.Add(op.StateCount.ToString() + " " + op.SegmentSize.ToString());
                ret.Add(String.Join("", actAutomate.ToArray()));
                /*for (int i = 0; i < op.SegmentSize; i++)
                {
                    ret.Add(op.GetHLines()[alp.code(i)]);
                }*/
                foreach (string s in op.GetHLines().Values)
                    ret.Add(s);
                foreach (string s in op.GetFLines())
                    ret.Add(s);
                foreach (Regex r in extAutomate)
                {
                    ret.Add(r.ToString().Replace('?', '*').TrimStart('^'));
                }
            return ret.ToArray();
        }
        public OperationSet GetOperation
        {
            get { return op; }
        }
        public List<Regex> GetExtinction
        {
            get { return extAutomate; }
        }
        public List<string> GetActivate
        {
            get { return actAutomate; }
        }
        public int GetSegmentSize
        {
            get { return kSegment; }
        }
        public List<List<string>> Grid = new List<List<string>>();
        private OperationSet op;
        private List<Regex> extAutomate;
        private List<string> actAutomate;
        private int kSegment;
        private Alphabet alp = new Alphabet();
    }
    public class MyErrors : Exception
    {
        public MyErrors(string st)
            : base(st)
        {
        }
    }
}

